package com.hbfec.fileserver.controller;

import com.hbfec.fileserver.callback.CustomUploadCallback;
import com.hbfec.fileserver.callback.IUploadLogicCallback;
import com.hbfec.fileserver.constant.*;
import com.hbfec.fileserver.json.Response;
import com.hbfec.fileserver.json.UploadSuccessResult;
import com.hbfec.fileserver.service.CacheService;
import com.hbfec.fileserver.service.FastDFSService;
import com.hbfec.fileserver.service.SimpleToolsService;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;


/**
 * 文件上传Controller
 */
@RestController
public class UpdateController {
    private static final Logger LOGGER = LoggerFactory.getLogger(UpdateController.class);

    @Value("${fileserver.charset}")
    private String charset;
    @Value("${fileserver.dateformat}")
    private String dateFormat;

    @Autowired
    private SimpleToolsService tools;
    @Autowired
    private CacheService cacheService;
    @Autowired
    private FastDFSService fastDFSService;

    /**
     * 普通文件上传；允许由客户端指定uuid
     * 写入缓存失败或上传FastDFS失败均不需要回滚，因为定时任务会结合（status!=FileStatus.UPLOADED and 超过过期时间）删除这些文件
     *
     * @param request  request
     * @param response response
     * @return 结果
     */
    @RequestMapping(value = "/update", method = RequestMethod.PUT, headers = "Content-Type=multipart/form-data")
    public Response upload(HttpServletRequest request, HttpServletResponse response) {
        final String uuid = tools.generateUUID();
        final String createDate = tools.dateToString(new Date(), dateFormat);
        try {
            return handleUpload(request, new IUploadLogicCallback() {
                @Override
                public Response handle(HashMap argMap, String fileName, InputStream inputStream) {
                    String realUuid = uuid;
                    // 初始化参数并验证
                    Long size = null;
                    Boolean expire = null;
                    try {
                        size = Long.valueOf((String) argMap.get("size"), 10);
                        expire = new Boolean((String) argMap.get("expire"));
                        if (argMap.get("expire") == null || size == null || size < 0 || fileName == null
                                || "".equals(fileName) || fileName.length() > Parameter.FILE_NAME_LENGTH_LIMIT
                                || inputStream == null) {
                            throw new Exception("");
                        }
                        if (argMap.get("uuid") != null && !"".equals(argMap.get("uuid"))) {
                            realUuid = (String) argMap.get("uuid");
                        }
                        if (realUuid.length() > Parameter.UUID_LENGTH_LIMIT) {
                            throw new Exception("");
                        }
                    } catch (Exception paramEx) {
                        return new Response(Tag.FAILURE, ErrorCode.INVALID_PARAMETER, "参数异常", null);
                    }
                    LOGGER.debug(String.format("待上传文件的信息如下：uuid=%s,size=%s,expire=%s,fileName=%s",
                            realUuid, size, expire, fileName));
                    // 将新的文件信息通过缓存插入数据库
                    try {
                        if (!cacheService.lpush(
                                String.format("%s%s_%s", CacheKeys.FILE_STATUS, realUuid, FileStatus.UPLOADING),
                                "", CacheKeys.FILE_QUEUE,
                                String.format("%s |  | %s | %s | %s | %s | %s", realUuid, fileName,
                                        size, createDate, expire ? "1" : "0", FileStatus.UPLOADING))) {
                            throw new Exception("新的文件信息插入入库缓存失败");
                        }
                    } catch (Exception cacheEx) {
                        return new Response(Tag.FAILURE, ErrorCode.UNKNOWN_ERROR, "新的文件信息插入入库缓存失败", null);
                    }
                    // 调用FastDFS上传文件
                    String[] urlParts = fastDFSService.uploadFile(size, tools.getFileNameSuffix(fileName),
                            new CustomUploadCallback(inputStream));
                    if (urlParts == null) {
                        return new Response(Tag.FAILURE, ErrorCode.UNKNOWN_ERROR, "文件上传FastDFS失败", null);
                    }
                    // 将url各部分进行拼装
                    StringBuilder stringBuilder = new StringBuilder();
                    for (String urlPart : urlParts) {
                        stringBuilder.append("/");
                        stringBuilder.append(urlPart);
                    }
                    String url = stringBuilder.toString();
                    // 调用FastDFS上传成功，将更新信息通过缓存写入数据库
                    try {
                        if (!cacheService.lpush(
                                String.format("%s%s_%s", CacheKeys.FILE_STATUS, realUuid, FileStatus.UPLOADED),
                                "", CacheKeys.FILE_QUEUE,
                                String.format("%s | %s | %s | %s | %s | %s | %s", realUuid, url, fileName,
                                        size, createDate, expire ? "1" : "0", FileStatus.UPLOADED))) {
                            throw new Exception("文件上传成功信息插入入库缓存失败");
                        }
                    } catch (Exception cacheEx) {
                        return new Response(Tag.FAILURE, ErrorCode.UNKNOWN_ERROR, "文件上传成功信息插入入库缓存失败", null);
                    }
                    return new Response(Tag.SUCCESS, ErrorCode.SUCCESS, "成功", new UploadSuccessResult(realUuid));
                }
            });
        } catch (Exception ex) {
            return new Response(Tag.FAILURE, ErrorCode.UNKNOWN_ERROR, "读取输入流失败", null);
        }
    }

    /**
     * 处理文件上传，必须保证表单数据在文件数据之前
     *
     * @param request  request
     * @param callback callback
     * @return 结果
     * @throws Exception Exception
     */
    private Response handleUpload(HttpServletRequest request, IUploadLogicCallback callback) throws Exception {
        HashMap argMap = new HashMap();
        // 实例化servletFileUpload
        ServletFileUpload servletFileUpload = new ServletFileUpload();
        // 初始化数据数据的迭代器
        FileItemIterator fileItemIterator = servletFileUpload.getItemIterator(request);
        // 声明迭代中需要的参数
        FileItemStream item = null;
        InputStream inputStream = null;
        // 开始迭代
        while (fileItemIterator.hasNext()) {
            item = fileItemIterator.next();
            inputStream = item.openStream();
            if (item.isFormField()) { // 表单数据
                argMap.put(item.getFieldName(), new String(IOUtils.toByteArray(inputStream), charset));
            } else { // 文件数据
                return callback.handle(argMap, item.getName(), inputStream);
            }
        }
        throw new Exception("没有从输入流中读取到文件");
    }
}